﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using WimaxSimulator.Wimax;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
using System.Runtime.Serialization;
using System.Windows.Forms;
using WimaxSimulator.Properties;

namespace WimaxSimulator
{
    public static class SimulationEngine
    {
        public delegate void SimulationTimeElapsed();
        public static event SimulationTimeElapsed SimulationTimeelepasedEvent;
        public static List<Station> stations = new List<Station>();


        public static List<MobileStation> MobileStations
        {
            get
            {
                return (from s in SimulationEngine.stations
                        where s is MobileStation
                        select s as MobileStation).ToList<MobileStation>();
            }
        }
        public static List<ISuperordinate> Superordinates
        {
            get
            {
                return (from n in stations
                        where n is ISuperordinate
                        select (ISuperordinate)n).ToList<ISuperordinate>();
            }
        }
        public static List<ISubordinate> Subordinates
        {
            get
            {
                return (from n in stations
                        where n is ISubordinate
                        select (ISubordinate)n).ToList<ISubordinate>();
            }
        }
        public static List<RelayStation> RelayStations
        {
            get
            {
                return (from s in stations
                        where s is RelayStation
                        select (RelayStation)s).ToList<RelayStation>();
            }
        }
        public static List<BaseStation> BaseStations
        {
            get
            {
                return (from s in stations
                        where s is BaseStation
                        select (BaseStation)s).ToList<BaseStation>();
            }
        }
        private static int lastID = 0;
        public static int NextId
        {
            get
            {
                return ++lastID;
            }
            set
            {
                lastID = value;
            }
        }
        private static Cidr lastAddressBlock;
        public static Cidr NextAddressBlock
        {
            get
            {
                if (lastAddressBlock == null)
                {
                    if (BaseStations.Count > 0)
                    {
                        lastAddressBlock = (from bs in BaseStations where bs.Ip == (from bs2 in BaseStations select bs2.Ip).Max() select bs.IpAddressBlock).Single<Cidr>();
                        lastAddressBlock.Ip += 0x00010000;
                    }
                    else
                        lastAddressBlock = new Cidr() { Ip = 0x0a110000, Subnet = 16, SubnetMask = 0xffff0000 };
                }
                Cidr c = new Cidr() { Ip = lastAddressBlock.Ip, Subnet = lastAddressBlock.Subnet, SubnetMask = lastAddressBlock.SubnetMask };
                lastAddressBlock.Ip += 0x00010000;

                return c;
            }
        }
        public static int SimulationTime { get; set; }
        public static bool SimulationRunning { get; set; }
        public static int SimulationDuration { get; set; }
        public static Dictionary<int, int> ReceivedData = new Dictionary<int, int>();
        public static Sink Sink = new Wimax.Sink(0, 0x10101010);
        public static bool RelayingMode { get; set; }
        private static int throughput;

        public static void run()
        {
            if (File.Exists("Log.log"))
                File.Delete("Log.log");

            if (!stations.Contains(Sink))
                stations.Add(Sink);
            foreach (Station s in stations)
            {
                ReceivedData.Add(s.Id, 0);
            }
            while (SimulationTime <= SimulationDuration)
            {
                NeighborAdvertisement();
                foreach (Station s in stations)
                {
                    s.Send();
                }
                SimulationTimeelepasedEvent();
                foreach (Station s in stations)
                {
                    Log(s.Id, ReceivedData[s.Id]);

                    ReceivedData[s.Id] = 0;
                }
                ReceivedData[Sink.Id] = 0;
                SimulationTime++;

            }
            showResults();

            ReceivedData.Clear();
        }

        private static void showResults()
        {
            int totalPacket=0;
            int totalDelay=0;
            throughput = 0;
            int congestion = 0;
            string res = "Id\tULSent\tULRecv\tDLSent\tDLRecv\n";
            foreach (Station s in stations)
            {
                if (s is MobileStation)
                {
                    totalPacket += ((MobileStation)s).TotalPacket;
                    totalDelay += ((MobileStation)s).TotalDelay;
                    throughput += s.TotalULReceived;
                }
                else if (s is RelayStation)
                {
                    RelayStation r = (RelayStation)s;
                    congestion += (from d in r.DownlinkQueue.Values select (from data in d select data.Size).Sum()).Sum();
                    congestion += (from d in r.UplinkQueue select d.Size).Sum();
                }
                else if (s is BaseStation)
                {
                    congestion += (from d in ((BaseStation)s).DownlinkQueue.Values select (from data in d select data.Size).Sum()).Sum();
                }

            }

            res += "Total throughput = " + throughput + "\tTotal congestion = " + congestion + "\n";
           

                Console.WriteLine(SimulationEngine.RelayingMode+"\t"+ Settings.Default.InRegionTrafficRate+"\t"+ Settings.Default.UlBw + "\t" + Settings.Default.DlBw + "\t" + Settings.Default.McsCoefficient + "\t" + Settings.Default.HopCountCoefficient + "\t" + Settings.Default.SentDataCoefficient + "\t" + Settings.Default.RecvDataCoefficient + "\t" + Settings.Default.LoadCoefficient + "\t" + Settings.Default.AccessChangeTreshold + "\t" + Settings.Default.MsTrafficBandwidth + "\t" + throughput + "\t" + congestion+"\t"+(double)totalDelay/totalPacket);

            
   
            if (Settings.Default.ShowDialog)
            {
                res += "Settings\n";
                res += "UL" + Settings.Default.UlBw + "\tDL:" + Settings.Default.DlBw + "\tMcs:" + Settings.Default.McsCoefficient + "\tHopCount:" + Settings.Default.HopCountCoefficient + "\tSentData:" + Settings.Default.SentDataCoefficient + "\tRecvData:" + Settings.Default.RecvDataCoefficient + "\tThreshold:" + Settings.Default.AccessChangeTreshold + "\n";
                MessageBox.Show(res);
            }
        }
        public static void Log(int id, string text)
        {


            //Console.WriteLine("{0}\tStation: {1}\t{2}", SimulationTime, id,text);

        }
        private static void Log(int id, int dataRate)
        {
        }
        public static void NeighborAdvertisement()
        {
            foreach (ISubordinate s in Subordinates)
            {
                Station sta = (Station)s;
                List<ISuperordinate> parents = (from p in Superordinates
                                                where sta != (Station)p && sta.InCoverage((Station)p) &&
                                                (p is BaseStation || (p is RelayStation && ((RelayStation)p).AccessStation != null && ((RelayStation)p).AccessStation != s))
                                                select p).ToList<ISuperordinate>();
                s.PathSelect(parents);
            }
        }




        public static uint GetAddressById(int p)
        {
            return (from s in stations
                    where s.Id == p
                    select s.Ip).SingleOrDefault<uint>();
        }



        public static void Clear()
        {
            foreach (Station s in stations)
                s.Clear();
            //lastID = 0;
            ReceivedData.Clear();
        }

        internal static Station GetStationById(int p)
        {
            return (from s in stations where s.Id == p select s).Single<Station>();
        }

        public static bool McsEnabled { get; set; }
    }
}
